	
import java.awt.Image;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;

import javax.media.opengl.GL;
import javax.media.opengl.glu.GLU;

import com.sun.opengl.util.texture.Texture;
import com.sun.opengl.util.texture.TextureData;
import com.sun.opengl.util.texture.TextureIO;

public class Grass {
	GL gl;
	GLU glu;
	private int shaderprogram, tex[];
	private int vert_shader, frag_shader;
	private int grass_num;
	private Vector3D grass[],pos;
	private int scale;
	private float cadr = 0.0f;
	private Mipmap mipmap;
	
	public Grass(GL gl_, GLU glu_){
		gl=gl_;
		glu=glu_;
		mipmap = new Mipmap(gl,glu,1);
		tex = new int[1];
		init();
		loadShader();
		CreateTexture(null);
	}
	private float distance(Vector3D a, Vector3D b) {
		Vector3D c = new Vector3D();
		c.x = a.x - b.x;
		c.y = a.y - b.y;
		c.z = 0f - b.z;
	    return (float) Math.sqrt((c.x()*c.x() + c.y()*c.y() + c.z()*c.z()));
	}
	
	public Vector3D[] qSort(Vector3D[] A, int low, int high) {
	      int i = low;                
	      int j = high;
	      float x = distance(A[(low+high)/2],pos); 
	      do {
	          while(distance(A[i],pos) > x) ++i; 
	          while(distance(A[j],pos) < x) --j;  
	          if(i <= j){           	             
	              Vector3D temp = new Vector3D();//quick sort for sprites
	              temp = A[i];
	              A[i] = A[j];
	              A[j] = temp;	             
	              i++; j--;
	          }
	      } while(i < j);
	      if(low < j) A = qSort(A, low, j);
	      if(i < high) A = qSort(A, i, high);
	      return A;
	  }

	private void init(){
		grass_num = 1000;//number of smoke clouds
		scale = 1;//sprite radius
		grass = new Vector3D[grass_num];
		pos = new Vector3D();
		grass_init();
	}
	public static float rnd() {
		return (float)Math.random();
	}
	private void grass_init(){
	    for(int i = 0; i < grass_num; i++) {
	        Vector3D data = new Vector3D();
	        data.setX(rnd()*230f-50f);
	        data.setY(rnd()*340f-50f);
	        data.setZ(rnd()+0.5f);
	        if(data.x > 0f && data.x < 90f && data.y > 0f && data.y < 90f) {
	        	i--;
	        	continue;
	        }
	        if(data.x > 0f && data.x < 90f && data.y > 160f && data.y < 250f) {
	        	i--;
	        	continue;
	        }
	        grass[i] = new Vector3D();
	        grass[i].add(data);
	    }
	}
	private void CreateTexture(Image image)
	 {
		URL imageURL= this.getClass().getResource(
				 "p_grass.png");
		 
		 gl.glGenTextures(1,tex, 0);
		 gl.glBindTexture(GL.GL_TEXTURE_2D, tex[0]);
		 
		 gl.glShadeModel(GL.GL_FLAT);
		 gl.glDepthFunc(GL.GL_LESS);
		 mipmap.makeImages(imageURL, 0);
		 mipmap.LoadImages(0);
//		 gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_LINEAR);
//		 gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,GL.GL_LINEAR);
	 }
	
	 private void loadShader(){
		 vert_shader = gl.glCreateShader(GL.GL_VERTEX_SHADER);
	        frag_shader = gl.glCreateShader(GL.GL_FRAGMENT_SHADER);
	        
	        BufferedReader brv = null;
			try {
				brv = new BufferedReader(
						new FileReader(
								"src/lighting.vert"));
			} catch (FileNotFoundException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
	        String vsrc = "";
	        String line;
	        try {
				while ((line=brv.readLine()) != null) {
				  vsrc += line + "\n";
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        gl.glShaderSource(vert_shader, 1,
	        		new String[] {vsrc}, (int[])null, 0);
	        gl.glCompileShader(vert_shader);
	        
	        BufferedReader brf = null;
			try {
				brf = new BufferedReader(
						new FileReader(
								"src/grass.frag"));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        String fsrc = "";
	        String lines;
	        try {
				while ((lines=brf.readLine()) != null) {
				  fsrc += lines + "\n";
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        gl.glShaderSource(frag_shader, 1,
	        		new String[] {fsrc}, (int[])null, 0);
	        gl.glCompileShader(frag_shader);
	        
	        shaderprogram = gl.glCreateProgram();
	        gl.glAttachShader(shaderprogram, vert_shader);
	        gl.glAttachShader(shaderprogram, frag_shader);
	        gl.glLinkProgram(shaderprogram);
	        gl.glValidateProgram(shaderprogram);
	 }
	 private void setShader(){

	        gl.glActiveTexture(GL.GL_TEXTURE0);
	        gl.glBindTexture(GL.GL_TEXTURE_2D,tex[0]);
	        gl.glUniform1i(gl.glGetUniformLocation(
	        		shaderprogram, "leaf"), 0);
	        
	        gl.glUniform3f(gl.glGetUniformLocation(
	        		shaderprogram, "eyePos"), pos.x,pos.y,pos.z);
	        gl.glUniform3f(gl.glGetUniformLocation(
	        		shaderprogram, "lightPos"), Atmosphere.sun.x,Atmosphere.sun.y,Atmosphere.sun.z);
	 }
	 public void draw(Camera camera, int reflect) {
		 		cadr+=0.06f;
		 		pos.setXYZ(Camera.pos.x,Camera.pos.y,Camera.pos.z);
			    Vector3D point1 = new Vector3D();
			    Vector3D point2 = new Vector3D();
			    Vector3D point3 = new Vector3D();
			    Vector3D point4 = new Vector3D();

			    Matrix4x4 matz = new Matrix4x4();
		    	
			    if(reflect == 0) {
			    	grass = qSort(grass,0,grass_num-1);
			    }
			    
			    gl.glUseProgram(shaderprogram);
			    setShader();
			    gl.glEnable(GL.GL_ALPHA_TEST);
			    gl.glEnable(GL.GL_BLEND);
			    gl.glBlendFunc(GL.GL_SRC_ALPHA,GL.GL_ONE_MINUS_SRC_ALPHA);
			    gl.glDepthMask(false);
			    gl.glDisable(GL.GL_CULL_FACE);
			    gl.glBegin(GL.GL_QUADS);
			    for(int i = 0; i< grass_num; i++) {
			    	point1.setXYZ(0,-scale,-scale);
				    point2.setXYZ(0,scale,-scale);
				    point3.setXYZ(0,(float) (scale*Math.sin(cadr)/2.0+scale),scale);
				    point4.setXYZ(0,(float) (scale*Math.sin(cadr)/2.0-scale),scale);
				    Matrix4x4 maty = new Matrix4x4(); 			    
				    maty = Matrix4x4.set((float)Math.cos(-camera.pitch),0,(float)Math.sin(-camera.pitch),0,
			                   0,1,0,0,
			                   (float)-Math.sin(-camera.pitch),0,(float)Math.cos(-camera.pitch),0,
			                   0,0,0,1);
			    

				    matz = Matrix4x4.set((float)Math.cos(-camera.yaw+Atmosphere.pi),(float)-Math.sin(-camera.yaw+Atmosphere.pi),0,0,
			    		(float)Math.sin(-camera.yaw+Atmosphere.pi),(float)Math.cos(-camera.yaw+Atmosphere.pi),0,0,
			                   0,0,1,0,
			                   0,0,0,1);
				    
				    point1 = Matrix4x4.mult(Matrix4x4.mult(matz,maty),point1);
				    point2 = Matrix4x4.mult(Matrix4x4.mult(matz,maty),point2);
				    point3 = Matrix4x4.mult(Matrix4x4.mult(matz,maty),point3);
				    point4 = Matrix4x4.mult(Matrix4x4.mult(matz,maty),point4);
				    
			    	float size = grass[i].z;
			    	float offset = 0f;
			    	if(size >= 1f) offset = 0.5f;
			    	if(distance(grass[i],pos) > 250.0f) continue;
					gl.glNormal3f(0f, 0f, 1f);
			    	gl.glTexCoord2d(offset,1);
			    	gl.glVertex3f(point1.x()*size+grass[i].x(),point1.y()*size+grass[i].y(),point1.z()*size+scale + 1f);

			    	gl.glTexCoord2d(offset+0.5f,1);
			    	gl.glVertex3f(point2.x()*size+grass[i].x(),point2.y()*size+grass[i].y(),point2.z()*size+scale + 1f);

			    	gl.glTexCoord2d(offset+0.5f,0);
			    	gl.glVertex3f(point3.x()*size+grass[i].x(),point3.y()*size+grass[i].y(),point3.z()*size+scale + 1f);

			    	gl.glTexCoord2d(offset,0);
			    	gl.glVertex3f(point4.x()*size+grass[i].x(),point4.y()*size+grass[i].y(),point4.z()*size+scale + 1f);
			    }
			    gl.glEnd();
			    gl.glDepthMask(true);
			    gl.glEnable(GL.GL_CULL_FACE);
			    gl.glDisable(GL.GL_ALPHA_TEST);
			    gl.glDisable(GL.GL_BLEND);
			    gl.glUseProgram(0);
	}
}